#include "./../../../include/online/Reactor/SocketIO.h"
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>

// 构造函数，初始化 SocketIO 对象
SocketIO::SocketIO(int fd)
    : _fd(fd) // 初始化成员变量 _fd，表示文件描述符
{
}

// 析构函数
SocketIO::~SocketIO()
{
    // 这里可以添加资源释放的逻辑，比如关闭文件描述符
}

// 读取指定长度的数据
int SocketIO::readn(char *buf, int len)
{
    int left = len;       // 剩余需要读取的字节数
    char *pstr = buf;     // 指向缓冲区的指针
    int ret = 0;          // 每次读取的字节数

    while (left > 0)      // 循环读取，直到读取完指定长度
    {
        ret = read(_fd, pstr, left); // 从文件描述符 _fd 读取数据
        if (ret == -1 && errno == EINTR)  // 如果读取被信号中断，重试
        {
            continue;
        }
        else if (ret == -1)  // 如果读取失败
        {
            perror("read error -1"); // 打印错误信息
            return -1;               // 返回错误
        }
        else if (ret == 0)  // 如果读取到文件末尾（对端关闭连接）
        {
            break;          // 退出循环
        }
        else                // 读取成功
        {
            pstr += ret;    // 移动缓冲区指针
            left -= ret;    // 减少剩余需要读取的字节数
        }
    }
    return len - left;      // 返回实际读取的字节数
}

// 写入指定长度的数据
int SocketIO::writen(const char *buf, int len)
{
    int left = len;         // 剩余需要写入的字节数
    const char *pstr = buf; // 指向缓冲区的指针
    int ret = 0;            // 每次写入的字节数

    while (left > 0)        // 循环写入，直到写入完指定长度
    {
        ret = write(_fd, pstr, left); // 向文件描述符 _fd 写入数据
        if (ret == -1 && errno == EINTR)  // 如果写入被信号中断，重试
        {
            continue;
        }
        else if (ret == -1)  // 如果写入失败
        {
            perror("write error -1"); // 打印错误信息
            return -1;                // 返回错误
        }
        else if (ret == 0)   // 如果写入 0 字节（通常不会发生）
        {
            break;           // 退出循环
        }
        else                 // 写入成功
        {
            pstr += ret;     // 移动缓冲区指针
            left -= ret;     // 减少剩余需要写入的字节数
        }
    }
    return len - left;       // 返回实际写入的字节数
}

// 按行读取数据，读取一行数据
int SocketIO::readLine(char *buf, int len)
{
    int left = len - 1;      // 剩余可以读取的字节数（留一个字节给字符串结束符 '\0'）
    char *pstr = buf;        // 指向缓冲区的指针
    int ret = 0, total = 0;  // ret 是每次读取的字节数，total 是总共读取的字节数

    while (left > 0)         // 循环读取，直到缓冲区满或遇到换行符
    {
        /*
        知识点：recv 的 MSG_PEEK 标志允许我们从缓冲区中读取数据，但不会移除数据。
        这样我们可以先检查是否有换行符，然后再决定是否读取。
        */
        ret = recv(_fd, pstr, left, MSG_PEEK); // 使用 MSG_PEEK 标志读取数据
        if (ret == -1 && errno == EINTR)       // 如果读取被信号中断，重试
        {
            continue;
        }
        else if (ret == -1)  // 如果读取失败
        {
            perror("readLine error -1"); // 打印错误信息
            return -1;                   // 返回错误
        }
        else if (ret == 0)   // 如果读取到文件末尾（对端关闭连接）
        {
            break;           // 退出循环
        }
        else                 // 读取成功
        {
            // 检查读取的数据中是否有换行符 '\n'
            for (int index = 0; index < ret; ++index)
            {
                if (pstr[index] == '\n')  // 找到换行符
                {
                    int size = index + 1; // 计算需要读取的字节数（包括换行符）
                    readn(pstr, size);    // 从缓冲区中移除数据
                    pstr += size;         // 移动缓冲区指针
                    *pstr = '\0';         // 添加字符串结束符
                    return total + size;  // 返回总共读取的字节数
                }
            }

            // 如果没有找到换行符，读取所有数据
            readn(pstr, ret); // 从缓冲区中移除数据
            total += ret;     // 更新总共读取的字节数
            pstr += ret;      // 移动缓冲区指针
            left -= ret;      // 减少剩余可以读取的字节数
        }
    }
    *pstr = '\0';             // 添加字符串结束符
    return total - left;      // 返回总共读取的字节数
}